package de.hs.rm;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Set;
import java.util.UUID;

import android.app.Application;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Handler;
import android.os.Message;
import android.widget.ArrayAdapter;
import android.widget.Toast;

public class BHMainApplication extends Application {
	
	private BluetoothAdapter mBluetoothAdapter;
    //private String address = "";
    private ArrayList<BluetoothDevice> btDevices;
    //private ArrayAdapter<String> btDevicesArrayAdapter;
	
    private boolean connected = false;
    private boolean serverActive = false;
    
	private BroadcastReceiver discoveryMonitor;
	private BroadcastReceiver discoveryResult;	
	private String dStarted = BluetoothAdapter.ACTION_DISCOVERY_STARTED;
	private String dFinished = BluetoothAdapter.ACTION_DISCOVERY_FINISHED;
	private String dFound = BluetoothDevice.ACTION_FOUND;
	
	private Handler currentHandler = null;
	private ConnectionThread connectionThread = null;	
	private CommunicationThread communicationThread = null;
	private ConnectedThread connectedThread = null;
    
	private class ConnectionThread extends Thread
    {
		
		@Override
		public void run() {
			//TODO: change to individual sendmessage calls so not all is done on the ui thread
			currentHandler.post(new Runnable() {
		          public void run() {
		        	  setupBluetooth();
		          }			
	       });
		}
		
		public void setupBluetooth()
	    {
	    	if(currentHandler != null)
	    	{
	    		mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
	    	
		        // If the adapter is null, then Bluetooth is not supported
		        if (mBluetoothAdapter == null) {
		            currentHandler.sendMessage(
		            		Message.obtain(currentHandler,
		            		BTMessage.BT_NOT_AVAILABLE));
		        }
		        
		        if(!mBluetoothAdapter.isEnabled())
		        {
		        	currentHandler.sendMessage(
		            		Message.obtain(currentHandler,
		            		BTMessage.BT_NOT_ENABLED));
		        }
		        else
		        {
		        	startBluetoothDiscovery();
		        }
	    	}
	    	
	    }
	    
	    private void startBluetoothDiscovery()
	    {	
	    	discoveryMonitor = new BroadcastReceiver()
	    	{
				@Override
				public void onReceive(Context context, Intent intent) {
					if(dStarted.equals(intent.getAction()))
					{
						currentHandler.sendMessage(
			            		Message.obtain(currentHandler,
			            		BTMessage.BT_DISCOVERY_STARTED));
					} else if(dFinished.equals(intent.getAction()))
					{
						unregisterReceiver(discoveryResult);
						unregisterReceiver(discoveryMonitor);
						mBluetoothAdapter.cancelDiscovery();
						
						//btDevices = testDevices(btDevices);
						
						currentHandler.sendMessage(
			            		Message.obtain(currentHandler,
			            		BTMessage.BT_DISCOVERY_FINISHED, btDevices));					
					}				
				}    		
	    	};
	    	
	    	registerReceiver(discoveryMonitor, new IntentFilter(dStarted));
	    	registerReceiver(discoveryMonitor, new IntentFilter(dFinished));
	    	
	    	discoveryResult = new BroadcastReceiver(){
				@Override
				public void onReceive(Context context, Intent intent) {
					BluetoothDevice remoteDevice;
					remoteDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
					if (remoteDevice.getBondState() != BluetoothDevice.BOND_BONDED)
						btDevices.add(remoteDevice);
						//btDevicesArrayAdapter.add(remoteDevice.getName() + "\n" + remoteDevice.getAddress());

					//Toast.makeText(context, "Device Discovered:" + remoteDeviceName , Toast.LENGTH_SHORT);
				}    		
	    	};    	
	    	registerReceiver(discoveryResult, new IntentFilter(dFound));
	    	
	    	if (mBluetoothAdapter.isDiscovering()) {
	    		mBluetoothAdapter.cancelDiscovery();
	        }
	    	btDevices = new ArrayList<BluetoothDevice>();
	    	btDevices.addAll(mBluetoothAdapter.getBondedDevices());
	    	
	   		mBluetoothAdapter.startDiscovery();
	    }
    }
    
    private class CommunicationThread extends Thread
    {
    	private BluetoothAdapter mBluetoothAdapter;
    	private BluetoothSocket btSocket = null;
        private OutputStream outStream = null;
        private InputStream inStream = null;
        private final UUID MY_UUID =
                        UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
        
        private String address = null;
        
        public CommunicationThread(String address)
        {
        	super();
        	this.address = address;
        }
    	
        @Override
		public void run() {
        
	    	mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
	    	BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
		    
	        try {
	            btSocket = device.createInsecureRfcommSocketToServiceRecord(MY_UUID);
	        } catch (IOException e) {}
	        
	        try {
	                btSocket.connect();
	                connected = true;
	               
	        } catch (IOException e) {
	            try {
	            	connected = false;
	            	btSocket.close();
	            	currentHandler.sendMessage(
		            		Message.obtain(currentHandler,HANDLER_MSG.CONNECTION_FAILED, "No Connection possible!"));
	            	return;
	            } catch (IOException e2) {}
	        }	        
	        setupBTConnection(btSocket);	        
	    }
		
		public void closeConnection()
	    {
	    	try {
	    		connected = false;
	    		btSocket.close();
	    		connectedThread.cancel();
	        } catch (IOException e2) {}
	    }
	    
	    
    }
    
    private void setupBTConnection(BluetoothSocket socket)
    {
    	connectedThread = new ConnectedThread(socket);
        connectedThread.start();
        connectedThread.writeMsg("ping:");
        try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		if(serverActive)
		{
			currentHandler.sendMessage(
            		Message.obtain(currentHandler,HANDLER_MSG.CONNECTION_ESTABLISHED));
		}
		else
			currentHandler.sendMessage(
            		Message.obtain(currentHandler,HANDLER_MSG.CONNECTION_FAILED, "No Server available!"));
			
    }
    
    private class ConnectedThread extends Thread {
        private final BluetoothSocket mmSocket;
        private final InputStream mmInStream;
        private final OutputStream mmOutStream;
     
        public ConnectedThread(BluetoothSocket socket) {
            mmSocket = socket;
            InputStream tmpIn = null;
            OutputStream tmpOut = null;
     
            // Get the input and output streams, using temp objects because
            // member streams are final
            try {
                tmpIn = socket.getInputStream();
                tmpOut = socket.getOutputStream();
            } catch (IOException e) { }
     
            mmInStream = tmpIn;
            mmOutStream = tmpOut;
        }
     
        public void run() {
            byte[] buffer = new byte[1024];  // buffer store for the stream
            int bytes; // bytes returned from read()
     
            // Keep listening to the InputStream until an exception occurs
            while (true) {
                try {
                    // Read from the InputStream
                    bytes = mmInStream.read(buffer);
                    String msg = new String(buffer, 0, bytes);
                    if(msg.contains("ping:"))
                    	serverActive = true;
                    if(msg.contains("disconnect:"))
                    {
                    	closeCommunication();
                    }
                    
                } catch (IOException e) {
                    break;
                }
            }
        }
     
        public void writeMsg(String msg)
	    {
	    	byte[] msgBuffer = msg.getBytes();
	        try {
	        	mmOutStream.write(msgBuffer);
	        } catch (IOException e) {}
	        
	        if (mmOutStream != null) {
	            try {
	            	mmOutStream.flush();
	            } catch (IOException e) {}
	        }
	    }
     
        /* Call this from the main activity to shutdown the connection */
        public void cancel() {
            try {
                mmSocket.close();
            } catch (IOException e) { }
        }
    }
    
    public BHMainApplication()
    {
    	super();
    }
        
    public void startConnectionThread()
    {
    	connectionThread = new ConnectionThread();
    	connectionThread.start();
    }
    
    public void startCommunicationThread(String address)
    {
    	communicationThread = new CommunicationThread(address);
    	communicationThread.start();
    }
    
//    public boolean testCommunication(String address)
//    {
//    	
//    	communicationThread = new CommunicationThread(address);
//        communicationThread.start();
//    	if(communicationThread != null)
//    		return communicationThread.testConnection();
//    	return false;
//    }
    
    public void closeCommunication()
    {
    	if(communicationThread != null)
    	{
    		communicationThread.closeConnection();
    		Intent intent = new Intent(getApplicationContext(), BHMainActivity.class);
    		intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    		startActivity(intent);
    		currentHandler.sendMessage(
            		Message.obtain(currentHandler,HANDLER_MSG.CONNECTION_LOST));
    	}
    }
    
    public void closeApp()
    {
    	communicationThread.closeConnection();
    }
    
    public void sendBTMessage(String msg)
    {
    	if(connectedThread != null)
    		connectedThread.writeMsg(msg);
    }
    
    
//    public void setAddress(String address)
//    {
//    	this.address = address;
//    }
    
    public void setCurrentHandler(Handler mHandler)
    {
    	this.currentHandler = mHandler;
    }

	public boolean isConnected() {
		return connected;
	}

}
